﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EpcTagLibrary
{
    public class SGTIN : EPC
    {
        private int m_Filter;
        private int m_Partition;
        private string m_CompanyPrefix;
        private string m_ItemReference;
        private string m_SerialNumber;

        public SGTIN(Bits bits)
            : base(bits)
        {
            Divide();
        }

        public SGTIN(byte[] data)
            : base(data)
        {
            Divide();
        }

        private void Divide()
        {
            m_Filter = (int)Common.BitsToInt(GetFieldBits(1));
            m_Partition = (int)Common.BitsToInt(GetFieldBits(2));

            m_CompanyPrefix = Common.BitsToPaddedInt(GetFieldBits(3), PartitionTable.GetCompanyPrefixLength(m_Partition));
            m_ItemReference = Common.BitsToPaddedInt(GetFieldBits(4), PartitionTable.GetItemRefLength(m_Partition));

            if (GetTagBitLength() == 96)
            {
                m_SerialNumber = Common.BitsToInt(GetFieldBits(5)).ToString();
            }
            else
            {
                m_SerialNumber = "";
                Bits B = GetFieldBits(5);

                for (int i = B.Length - 7; i >= 0; i -= 7)
                {
                    Bits F = B.GetBits(i, i + 6);
                    char C = (char)Common.BitsToInt(F);

                    if (Common.IsLegalChar(C))
                    {
                        m_SerialNumber = C + m_SerialNumber;
                    }
                }
            }
        }

        public int Filter
        {
            get
            {
                return m_Filter;
            }
        }

        public int Partition
        {
            get
            {
                return m_Partition;
            }
        }

        public string CompanyPrefix
        {
            get
            {
                return m_CompanyPrefix;
            }
        }

        public string ItemReference
        {
            get
            {
                return m_ItemReference;
            }
        }

        public string SerialNumber
        {
            get
            {
                return m_SerialNumber;
            }
        }

        public string GetGTIN14()
        {
            StringBuilder Result = new StringBuilder(13);
            Result.Append(ItemReference[0]);
            Result.Append(CompanyPrefix);
            for (int i = 1; i < ItemReference.Length; i++)
            {
                Result.Append(ItemReference[i]);
            }

            int Sum = Result[0] * 3;
            Sum += Result[1] * 1;
            Sum += Result[2] * 3;
            Sum += Result[3] * 1;
            Sum += Result[4] * 3;
            Sum += Result[5] * 1;
            Sum += Result[6] * 3;
            Sum += Result[7] * 1;
            Sum += Result[8] * 3;
            Sum += Result[9] * 1;
            Sum += Result[10] * 3;
            Sum += Result[11] * 3;
            Sum += Result[12] * 1;

            int D14 = (int)((Math.Ceiling(((double)Sum) / 10) * 10) - Sum);

            Result.Append(Math.Abs(D14));

            return Result.ToString();
        }

        public override int[] GetFieldSplits()
        {
            Bits Data = GetBits();
            int BitCount = Data.Length;

            int[] Result = new int[5];

            Result[0] = (BitCount == 96 ? 88 : 190);
            Result[1] = (BitCount == 96 ? 85 : 187);

            Bits PartitionBits = Data.GetBits((BitCount == 96 ? 82 : 184), (BitCount == 96 ? 82 : 184) + 2);
            int Partition = (int)Common.BitsToInt(PartitionBits);
            int CoBitCount = PartitionTable.GetCompanyPrefixBitCount(Partition);


            Result[2] = (BitCount == 96 ? 82 : 184);
            Result[3] = (BitCount == 96 ? 82 : 184) - CoBitCount;
            Result[4] = (BitCount == 96 ? 38 : 140);

            return Result;           
        }

        public override Uri GetTag()
        {
            StringBuilder Result = new StringBuilder();

            Result.Append("urn:epc:id:");
            Result.Append(GetSchemeName());
            Result.Append(":");
            Result.Append(CompanyPrefix);
            Result.Append(".");
            Result.Append(ItemReference);
            Result.Append(".");
            Result.Append(SerialNumber);

            return new Uri(Result.ToString());
        }

        public override Uri GetEPC()
        {
            StringBuilder Result = new StringBuilder();

            Result.Append("urn:epc:id:");
            Result.Append(GetScheme());
            Result.Append(":");
            Result.Append(CompanyPrefix);
            Result.Append(".");
            Result.Append(ItemReference);
            Result.Append(".");
            Result.Append(SerialNumber);

            return new Uri(Result.ToString());
        }

        public override string GetScheme()
        {
            return GetSchemeName() + "-" + GetTagBitLength();
        }

        public override string GetSchemeName()
        {
            return "sgtin";
        }

        private static class PartitionTable
        {
            public static int GetCompanyPrefixBitCount(int partition)
            {
                switch (partition)
                {
                    case 0:
                        return 40;
                    case 1:
                        return 37;
                    case 2:
                        return 34;
                    case 3:
                        return 30;
                    case 4:
                        return 27;
                    case 5:
                        return 24;
                    case 6:
                        return 20;
                    default:
                        throw new EPCParseException("Invalid Partition Code:" + partition);
                }
            }

            public static int GetCompanyPrefixLength(int partition)
            {
                if ((partition >= 0) && (partition <= 6))
                {
                    return 12 - partition;
                }

                throw new EPCParseException("Invalid Partition Code:" + partition);
            }

            public static int GetItemRefBitCount(int partition)
            {
                switch (partition)
                {
                    case 0:
                        return 4;
                    case 1:
                        return 7;
                    case 2:
                        return 10;
                    case 3:
                        return 14;
                    case 4:
                        return 17;
                    case 5:
                        return 20;
                    case 6:
                        return 24;
                    default:
                        throw new EPCParseException("Invalid Partition Code:" + partition);
                }
            }

            public static int GetItemRefLength(int partition)
            {
                if ((partition >= 0) && (partition <= 6))
                {
                    return partition + 1;
                }

                throw new EPCParseException("Invalid Partition Code:" + partition);
            }

            public static int GetPartition(string companyPrefix)
            {
                int Length = companyPrefix.Length;

                if ((Length >= 6) && (Length <= 12))
                {
                    return 12 - Length;
                }

                throw new EPCParseException("Invalid Company Prefix:" + companyPrefix);
            }
        }
    }
}
